package com.chat.transmission.config;

import com.chat.common.dto.MessageDTO;
import com.chat.common.enums.MsgContentItemType;
import com.chat.common.enums.UdpInfoType;
import com.chat.common.vo.MsgContentItem;
import com.chat.transmission.util.RedisHashUtil;
import com.google.gson.Gson;
import jakarta.annotation.Resource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

//test1:客户端 - 服务器1 服务器1.2 - 客户端 能为全锥形、ip限制 不能为端口限制、对称
//test2:客户端 - 服务器1 客户端 - 服务器1.2 能为全锥形、ip限制、端口限制 不能为对称
//test3:客户端 - 服务器1 服务器1 - 服务器2 服务器2 - 客户端 能为全锥形 不能为ip限制、端口限制、对称
//以客户端能不能接受到回复来作为判断
//    if (test1 && test2 && test3) {
//        natType = NatType.FullCone;全锥型
//    } else if (!test1 && !test2 && !test3) {
//        natType = NatType.Symmetric;对称型
//    } else if (!test1 && test2 && !test3) {
//        natType = NatType.PortRestrictedCone;端口限制型
//    } else if (test1 && test2 && !test3) {
//        natType = NatType.RestrictedCone;ip限制型
//    } else {
//        natType = NatType.Unknown;测试出错
//    }

@Configuration
public class UdpConfig implements CommandLineRunner {

    @Value("${udp.port1}")
    private int udpPort1;
    @Value("${udp.port2}")
    private int udpPort2;
    @Value("${udp.port3}")
    private int udpPort3;
    @Value("${udp.server}")
    private String anotherUdpServerIp;

    @Resource
    private RedisHashUtil redisHashUtil;
    @Resource
    private ThreadPoolTaskExecutor taskExecutor;


    @Override
    public void run(String... args) throws Exception {

        //用来存取用户的ip，地址和端口，做检测
        Map<String, String> userIdMap = new ConcurrentHashMap<>();
        //创建socket对象和数据包
        DatagramSocket datagramSocket1 = new DatagramSocket(udpPort1);
        DatagramSocket datagramSocket2 = new DatagramSocket(udpPort2);
        DatagramSocket datagramSocket3 = new DatagramSocket(udpPort3);
        byte[] bytePacket1 = new byte[1024];
        byte[] bytePacket2 = new byte[1024];
        byte[] bytePacket3 = new byte[1024];
        DatagramPacket datagramPacket1 = new DatagramPacket(bytePacket1, bytePacket1.length);
        DatagramPacket datagramPacket2 = new DatagramPacket(bytePacket2, bytePacket2.length);
        DatagramPacket datagramPacket3 = new DatagramPacket(bytePacket3, bytePacket3.length);
        //nat测试
        taskExecutor.execute(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        datagramSocket1.receive(datagramPacket1);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                    //获取客户端发过来的内容
                    String data = new String(datagramPacket1.getData(), 0, datagramPacket1.getLength());
                    //拆分内容，只会有两部分，id和次数
                    String[] contents = data.split(":");
                    //当发现为第一次测试，会通知端口2回复信息
                    if (contents[1].equals("1")) {
                        DatagramPacket sendPacket = new DatagramPacket(contents[0].getBytes(), contents[0].getBytes().length,
                                new InetSocketAddress(datagramPacket1.getAddress().getHostAddress(), datagramPacket1.getPort()));
                        try {
                            datagramSocket2.send(sendPacket);
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }
                    //当发现为第二次测试，会比较端口1和2接受的数据包的ip和端口进行判断
                    else if (contents[1].equals("2")) {
                        String userIp = datagramPacket1.getAddress().getHostAddress() + ":" + datagramPacket1.getPort();
                        //map集合会以客户端id为键，IP和端口为值，没有则往map里放入，有取出来和当前接收到的数据包内容做IP和端口比较
                        if (userIdMap.containsKey(contents[0])) {
                            if (userIp.equals(userIdMap.get(contents[0]))) {
                                DatagramPacket sendPacket = new DatagramPacket(contents[0].getBytes(), contents[0].getBytes().length,
                                        new InetSocketAddress(datagramPacket1.getAddress().getHostAddress(), datagramPacket1.getPort()));
                                try {
                                    datagramSocket2.send(sendPacket);
                                } catch (IOException e) {
                                    throw new RuntimeException(e);
                                }
                            }
                            userIdMap.remove(contents[0]);
                        } else {
                            userIdMap.put(contents[0], userIp);
                        }
                    }
                    //当发现为第三次测试，会通知另一个服务器去回复消息
                    else if (contents[1].equals("3")) {
                        String userIdAndIp = contents[0] + ":" + datagramPacket1.getAddress().getHostAddress() + ":" + datagramPacket1.getPort();
                        DatagramPacket sendPacket = new DatagramPacket(userIdAndIp.getBytes(), userIdAndIp.getBytes().length,
                                new InetSocketAddress(anotherUdpServerIp, udpPort1));
                        try {
                            datagramSocket2.send(sendPacket);
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        });
        //nat测试
        taskExecutor.execute(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        datagramSocket2.receive(datagramPacket2);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                    //获取客户端发过来的内容
                    String data = new String(datagramPacket2.getData(), 0, datagramPacket2.getLength());
                    //拆分内容，只会有两部分，id和次数
                    String[] contents = data.split(":");
                    //当发现为第二次测试，会比较端口1和2接受的数据包的ip和端口进行判断
                    if (contents[1].equals("2")) {
                        String userIp = datagramPacket2.getAddress().getHostAddress() + ":" + datagramPacket2.getPort();
                        //map集合会以客户端id为键，IP和端口为值，没有则往map里放入，有取出来和当前接收到的数据包内容做IP和端口比较
                        if (userIdMap.containsKey(contents[0])) {
                            if (userIp.equals(userIdMap.get(contents[0]))) {
                                DatagramPacket sendPacket = new DatagramPacket(contents[0].getBytes(), contents[0].getBytes().length,
                                        new InetSocketAddress(datagramPacket2.getAddress().getHostAddress(), datagramPacket2.getPort()));
                                try {
                                    datagramSocket2.send(sendPacket);
                                } catch (IOException e) {
                                    throw new RuntimeException(e);
                                }
                            }
                            userIdMap.remove(contents[0]);
                        } else {
                            userIdMap.put(contents[0], userIp);
                        }
                    }
                }
            }
        });
        //转发通话的udp包，前23位数据被用作地址，其中会e补足不够23位的地址
        //第一位用来表示动作 1表示存进redis，2表示转发
        //1：需要是否为群电话（1群电话，2个人电话）发送者token，接收id，nat类型
        //2：需要接收ip和端口，以及通话内容。需 4 + 4 + 4 + 4 + 5 位做ip和端口
        taskExecutor.execute(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        datagramSocket3.receive(datagramPacket3);
                    } catch (IOException e) {
                        throw new RuntimeException(e);
                    }
                    byte[] bytes = datagramPacket3.getData();
                    String type = new String(bytes, 0, 1);
                    if (UdpInfoType.toRedis.equals(type)) {
                        String info = new String(bytes, 0, datagramPacket3.getLength());
                        System.out.println(info);
                        //这个数组会有多个部分，0判断udp包类型（已经判断），1判断是否为群电话，后面分别为MessageDTO各个变量内容（按顺序），除了id, time, isGroup
                        String[] contents = info.split(":");
                        //包装信息存入redis
                        MessageDTO messageDTO = new MessageDTO();
                        MsgContentItem msgContentItem = new MsgContentItem();
                        msgContentItem.setType(MsgContentItemType.natType);
                        List<MsgContentItem> list = new ArrayList<MsgContentItem>();
                        list.add(msgContentItem);
                        messageDTO.setContentItemList(list);
                        //群电话
                        if (UdpInfoType.group.equals(contents[1])) {
                            messageDTO.setTime(LocalDateTime.now().toString());
                            messageDTO.setIsGroup(true);
                            msgContentItem.setContext(datagramPacket3.getAddress() + ":" + datagramPacket3.getPort() + ":" + contents[2]);
                            messageDTO.setUserToken(contents[3]);
                            messageDTO.setUserName(contents[4]);
                            messageDTO.setUserAvatar(contents[5] + ":" +contents[6]);
                            messageDTO.setToUserAccount(contents[7]);
                        }
                        //个人电话
                        else if (UdpInfoType.user.equals(contents[1])) {
                            messageDTO.setTime(LocalDateTime.now().toString());
                            messageDTO.setIsGroup(false);
                            msgContentItem.setContext(datagramPacket3.getAddress() + ":" + datagramPacket3.getPort() + ":" + contents[2]);
                            messageDTO.setUserToken(contents[3]);
                            messageDTO.setUserName(contents[4]);
                            messageDTO.setUserAvatar(contents[5] + ":" +contents[6]);
                            messageDTO.setToUserAccount(contents[7]);
                        }
                        System.out.println(new Gson().toJson(messageDTO));
                        redisHashUtil.publishMessage(messageDTO);
                    } else if (UdpInfoType.transmit.equals(type)) {
                        String ipAndPort = new String(bytes, 2, 23);
                        System.out.println(ipAndPort);
                        ipAndPort = ipAndPort.replace("e", "");
                        String[] contents = ipAndPort.split(":");
                        DatagramPacket sendPacket = new DatagramPacket(bytes, 23, datagramPacket3.getLength() - 24,
                                new InetSocketAddress(contents[0], Integer.valueOf(contents[1])));
                        try {
                            datagramSocket3.send(sendPacket);
                        } catch (IOException e) {
                            throw new RuntimeException(e);
                        }
                    }
                }
            }
        });
    }
}
